#define WIN32_LEAN_AND_MEAN
#include "windows.h"

#include "stdlib.h"
#include "stdio.h"

#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <fstream>
#include <sstream>

#include "log.h"

#define MAX_CONSOLE_LINES	256
#define LOG_BUFFER_SIZE		81920
#define LOG_FILENAME		"applog.txt"

static std::ofstream sLogFile;
static bool sClearFile = true;
static bool sInitialized = false;

static CRITICAL_SECTION gCS;

namespace Tmpl {

void Enter()
{
	if (!sInitialized)
	{
		InitializeCriticalSection(&gCS);
		sInitialized = true;
	}
	EnterCriticalSection(&gCS);
}
void Leave()
{
	LeaveCriticalSection(&gCS);
}

void Log::OpenConsole()
{
	int hConHandle;
	HANDLE lStdHandle;
	CONSOLE_SCREEN_BUFFER_INFO coninfo;
	FILE *fp;

	AllocConsole();
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&coninfo);
	coninfo.dwSize.Y = MAX_CONSOLE_LINES;
	SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coninfo.dwSize);

	lStdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
	hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
	fp = _fdopen(hConHandle, "w");
	*stdout = *fp;
	setvbuf(stdout, NULL, _IONBF, 0);

	lStdHandle = GetStdHandle(STD_INPUT_HANDLE);
	hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
	fp = _fdopen(hConHandle, "r");
	*stdin = *fp;
	setvbuf(stdin, NULL, _IONBF, 0);

	lStdHandle = GetStdHandle(STD_ERROR_HANDLE);
	hConHandle = _open_osfhandle((intptr_t)lStdHandle, _O_TEXT);
	fp = _fdopen(hConHandle, "w");
	*stderr = *fp;
	setvbuf(stderr, NULL, _IONBF, 0);

	std::ios::sync_with_stdio();
}

void Log::Write(const char* message)
{
	const char* mode = sClearFile ? "w" : "a";
	if(sClearFile) sClearFile = false;

	Enter();

	FILE* fp; fopen_s(&fp, LOG_FILENAME, mode);
	std::cout << message << std::endl;
	fprintf(fp, "%s\n", message);
	fclose(fp);

	Leave();
}

void Log::Print(const char* format, ...)
{
	Enter();
	char buffer[LOG_BUFFER_SIZE];

	va_list args;
	va_start(args, format);
	vsprintf_s(buffer, format, args);
	va_end(args);

	Leave();

	Log::Write(buffer);
}

void Log::ErrorFL(const char* file, int line, const char* format, ...)
{
	Enter();

	char buffer[LOG_BUFFER_SIZE];

	sprintf_s(buffer, "Error occured in \"%s\" at line %i:\n", file, line);
	const int printPos = (int)strlen(buffer);

	va_list args;
	va_start(args, format);
	vsprintf_s(&buffer[printPos], LOG_BUFFER_SIZE-printPos, format, args);
	va_end(args);

	Leave();

	Log::Write(buffer);
	::MessageBoxA(NULL, buffer, "Error", MB_ICONERROR | MB_OK);


}

}; // namespace tmpl